package com.astersoft.lib.generator;

/*
 * Copyright 2012 AsterSoft personal developer - Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import com.astersoft.lib.generator.algorithm.MersenneTwisterFast;

/**
 * A simple random password generator, user can specify the length.
 * Default length is 8, the common UNIX system effective password length.
 * 
 * @author dario durzo
 * @version 0.1
 * @date 26-08-2009
 */
public class ASPasswordGen {
	public static final int MAX_ASCII_CHAR_CODE = 122;
	public static final int MIN_ASCII_CHAR_CODE = 48;
	public static final int[] EXCLUSION_LIST = new int[] {58, 59, 60, 61, 62, 91, 92, 93, 94, 96};
	public static final int[] FULL_EXCLUSION_LIST = new int[] {58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96};
	
	/**
	 * Generate random password of certain length
	 * 
	 * @param n - the password length
	 * @return the password generated
	 */
	public static String getPassword(int n) {
		char[] pw = new char[n];
		int c = 'A';
		int r1 = 0;
		for (int i = 0; i < n; i++) {
			r1 = (int) (Math.random() * 3);
			switch (r1) {
			case 0:
				c = '0' + (int) (Math.random() * 10);
				break;
			case 1:
				c = 'a' + (int) (Math.random() * 26);
				break;
			case 2:
				c = 'A' + (int) (Math.random() * 26);
				break;
			}
			pw[i] = (char) c;
		}
		return new String(pw);
	}
	
	/**
	 * Get a real random password.
	 * This password contains at least 
	 * 1 number
	 * 1 lowerCase char
	 * 1 upperCase char
	 * 
	 * @param n
	 * 		minLenght is 3
	 * @return
	 * @throws IllegalArgumentException
	 * 		if n < 3
	 */
	public static String getRealRandomPassword(int n) {
		if(n < 3) throw new IllegalArgumentException("Password lenght must be at least 3 chars");

		MersenneTwisterFast mtf = new MersenneTwisterFast((new Random()).nextInt(200));
		char[] pw = new char[n];
		int c = 'A';
		int r1 = 0;
		Map<Integer, Integer> rightCharMap = new HashMap<Integer, Integer>();
		for(int i = 0; i < n || rightCharMap.size() < 3; i++) {
			// reset to zero if first condition getted
			if(i >= n) i = 0;
			
			// if position is already get, continue
			if(rightCharMap.containsValue(i)) continue;
			
			// get random position
			r1 = mtf.nextInt(3);
			if(!rightCharMap.containsKey(r1)) rightCharMap.put(r1, i);
			switch (r1) {
			case 0:
				c = '0' + mtf.nextInt(10);
				break;
			case 1:
				c = 'a' + mtf.nextInt(26);
				break;
			case 2:
				c = 'A' + mtf.nextInt(26);
				break;
			}
			pw[i] = (char) c;
		}
		
		return new String(pw);
	}
	
	/**
	 * Generate real random password
	 * 
	 * @param n - the password length
	 * @return the password
	 */
	public static String getRestictedRandomPWD(int n) {
		String returnStr = "";
		MersenneTwisterFast mtf = new MersenneTwisterFast((new Random()).nextInt(200));
		for(int i = 0; i < n; i++){
			int rand = mtf.nextInt(MAX_ASCII_CHAR_CODE);
			while(!ASPasswordGen.isValid(rand, FULL_EXCLUSION_LIST)) rand = mtf.nextInt(MAX_ASCII_CHAR_CODE);
			returnStr += (char)rand;
		}
		
		return returnStr;
	}
	
	/**
	 * Get random password without full restrictions
	 * @param n
	 * @return
	 */
	public static String genRandomPWD(int n){
		String returnStr = "";
		MersenneTwisterFast mtf = new MersenneTwisterFast((new Random()).nextInt(200));
		for(int i = 0; i < n; i++){
			int rand = mtf.nextInt(MAX_ASCII_CHAR_CODE);
			while(!ASPasswordGen.isValid(rand)) rand = mtf.nextInt(MAX_ASCII_CHAR_CODE);
			returnStr += (char)rand;
		}
		
		return returnStr;
	}
	
	/**
	 * Check if number is ASCII valid
	 * 
	 * @param number
	 * @return
	 */
	private static boolean isValid(int number) {
		return isValid(number, EXCLUSION_LIST);
	}
	private static boolean isValid(int number, int[] exludeList){
		if(number < MIN_ASCII_CHAR_CODE || number > MAX_ASCII_CHAR_CODE)
			return false;
		
		Arrays.sort(exludeList);
		if(Arrays.binarySearch(exludeList, number) >= 0){
			return false;
		}
		
		return true;
	}
	
	/**
	 * Test point
	 * 
	 * @param args
	 */
	public static void main(String[] args){
		for(int i = 0; i < 200; i++)
			System.out.println(ASPasswordGen.getRealRandomPassword(8));
	}
}